package com.orange.adminapi.service.admin;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.orange.adminapi.pojo.req.admin.AdminChangePasswordReq;
import com.orange.adminapi.pojo.req.admin.AdminSearchReq;
import com.orange.adminapi.service.permission.AdminRoleService;
import com.orange.adminapi.service.permission.RoleService;
import com.orange.core.mapper.admin.AdminMapper;
import com.orange.core.pojo.PageReqDto;
import com.orange.core.pojo.R;
import com.orange.core.pojo.entity.admin.Admin;
import com.orange.core.pojo.entity.permission.AdminRole;
import com.orange.core.pojo.entity.permission.Role;
import com.orange.core.pojo.enums.EStatus;
import com.orange.core.util.Constants;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class AdminService extends ServiceImpl<AdminMapper, Admin> {

    private final AdminRoleService adminRoleService;
    private final RoleService roleService;
    private final PasswordEncoder passwordEncoder;
    private final StringRedisTemplate redisTemplate;

    public Page<Admin> adminPage(PageReqDto<AdminSearchReq> pageReqDto) {
        LambdaQueryChainWrapper<Admin> wrapper = this.lambdaQuery()
                .orderByDesc(Admin::getCreateTime);
        AdminSearchReq condition = pageReqDto.getCondition();
        if (condition != null) {
            wrapper.like(StrUtil.isNotBlank(condition.getName()), Admin::getName, condition.getName())
                    .like(StrUtil.isNotBlank(condition.getLoginName()), Admin::getLoginName, condition.getLoginName())
                    .like(StrUtil.isNotBlank(condition.getPhone()), Admin::getPhone, condition.getPhone())
                    .eq(condition.getStatus() != null, Admin::getStatus, condition.getStatus());
        }
        Page<Admin> page = wrapper.page(pageReqDto.toPage());
        List<Admin> records = page.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<Integer> adminIds = records.stream().map(Admin::getId).toList();
            List<AdminRole> adminRoles = adminRoleService.lambdaQuery()
                    .in(AdminRole::getAdminId, adminIds)
                    .list();
            Map<Integer, List<Integer>> adminRoleMap = adminRoles.stream().collect(Collectors.groupingBy(AdminRole::getAdminId, Collectors.mapping(AdminRole::getRoleId, Collectors.toList())));
            List<Integer> roleIds = adminRoles.stream().map(AdminRole::getRoleId).distinct().toList();
            if(CollUtil.isNotEmpty(roleIds)){
                Map<Integer, String> roleNameMap = roleService.listByIds(roleIds).stream().collect(Collectors.toMap(Role::getId, Role::getName));
                for (Admin admin : records) {
                    admin.setRoles(adminRoleMap.getOrDefault(admin.getId(), new ArrayList<>()).stream().map(roleNameMap::get).toList());
                }
            }else {
                for (Admin admin : records) {
                    admin.setRoles(new ArrayList<>());
                }
            }
        }
        return page;
    }

    public Admin detail(Admin admin) {
        if (admin != null) {
            List<Integer> roleIds = adminRoleService.lambdaQuery()
                    .eq(AdminRole::getAdminId, admin.getId())
                    .list().stream().map(AdminRole::getRoleId).collect(Collectors.toList());
            admin.setRoleIds(roleIds);
        }
        return admin;
    }

    @Transactional
    @Override
    public boolean save(Admin admin) {
        if (StrUtil.isNotBlank(admin.getPlainPassword())) {
            admin.setPassword(passwordEncoder.encode(admin.getPlainPassword()));
        }
        boolean result = super.save(admin);
        if (CollUtil.isNotEmpty(admin.getRoleIds())) {
            List<AdminRole> adminRoles = admin.getRoleIds().stream().map(rId -> new AdminRole(admin.getId(), rId)).collect(Collectors.toList());
            adminRoleService.saveBatch(adminRoles);
        }
        return result;
    }


    @Transactional
    public boolean update(Admin admin) {
        if (StrUtil.isNotBlank(admin.getPlainPassword())) {
            admin.setPassword(passwordEncoder.encode(admin.getPlainPassword()));
        }
        boolean result = this.updateById(admin);
        adminRoleService.remove(new LambdaQueryWrapper<AdminRole>().eq(AdminRole::getAdminId, admin.getId()));
        if (CollUtil.isNotEmpty(admin.getRoleIds())) {
            List<AdminRole> adminRoles = admin.getRoleIds().stream().map(rId -> new AdminRole(admin.getId(), rId)).collect(Collectors.toList());
            adminRoleService.saveBatch(adminRoles);
        }
        return result;
    }

    @Transactional
    public boolean setStatus(int id, EStatus status) {
        if (status != null) {
            // 清空token
            Admin admin = this.getById(id);
            BoundSetOperations<String, String> ops = redisTemplate.boundSetOps(Constants.ADMIN_TOKEN_REDIS_KEY + admin.getLoginName());
            String token;
            while (StrUtil.isNotBlank(token = ops.pop())) {
                redisTemplate.delete(Constants.ADMIN_TOKEN_REDIS_KEY + token);
            }
            redisTemplate.delete(Constants.ADMIN_TOKEN_REDIS_KEY + admin.getLoginName());

            return this.updateById(new Admin().setId(id).setStatus(status.getValue()));
        }
        return false;
    }

    @Transactional
    public R<Boolean> changePassword(AdminChangePasswordReq req) {
        Admin admin = this.getById(req.getId());
        if (!req.getNewPassword().equals(req.getRepeatNewPassword())) {
            throw new IllegalArgumentException("输入的两次密码不一致");
        }
        String newPassword = passwordEncoder.encode(req.getNewPassword());
        return R.ok(this.updateById(new Admin().setId(admin.getId()).setPassword(newPassword)));
    }
}
